CloudFormationで提供されていない処理をカスタムリソースで作ってみた。
※本ブログの内容は自己責任で検証また実施ください。
先日、Amazon Inspector を CloudFormationで一撃設定してみる(SNS手動)を 公開したところ、手動がはいるなら、一撃じゃないじゃんというツッコミを盛大に頂いたので 改めて、一撃で頑張ってみます。
InspectorのSNSトピックの設定は現在CloudFormationで提供されていないんですが、 その部分をCloudFormationのカスタムリソースで作成してみました。
カスタムリソースは諸刃の剣に近い印象なので、 基本的にはサポートされていない処理、機能を積極的に組み込むのは推奨できませんが、 どうしても一撃で。とか、カスタムリソースのサンプルが欲しかった!!!とかいう人の役に立てば幸いです。
カスタムリソースとは
さて、カスタムリソースって何っていう人は ここをご参照ください https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html
カスタムリソースを使用すると、テンプレートにカスタムのプロビジョニングロジックを記述し、ユーザーがスタックを作成、更新(カスタムリソースを変更した場合)、削除するたびに AWS CloudFormation がそれを実行します。 たとえば、AWS CloudFormation のリソースタイプとして使用できないリソースを含める必要があるとします。それらのリソースは、カスタム リソースを使用して含めることができます。この方法により、すべての関連リソースを 1 つのスタックで管理できます。
今回はサポートされていないリソースというよりは、単純な設定なのであまり当てはまりませんが、 カスタムリソースの実装の仕方という意味では、わかりやすいものになるかと思います。
ではさっそく。
作ってみる
今回はAWS Lambda-backed カスタムリソース
を使用します。
実装したい処理はInspectorのSNS トピックの設定になります。
APIリファレンスから該当の処理を探します。 https://docs.aws.amazon.com/ja_jp/inspector/latest/APIReference/API_SubscribeToEvent.html
今回はPythonで実装するので Python SDK (Boto3) https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector.html#Inspector.Client.subscribe_to_event
のサンプルを見つけます。
サンプル処理をまんま参考にしつつ、処理をPythonで実装して、CloudFormation内でLambdaとして定義します
処理に渡すパラメータはCloudFormationからのパラメータから'ServiceToken'のみ削ってはほぼそのまま渡して
そのまま、返しています。
それなりな人は、それなりな感じに修正してください。
InspectorSubscribeToEventLambda: Type: AWS::Lambda::Function Properties: Code: ZipFile: | import json import boto3 import cfnresponse def handler(event, context): try: print(event) params = dict([(k, v) for k, v in event['ResourceProperties'].items() if k != 'ServiceToken']) # print(params) client = boto3.client('inspector') if event['RequestType'] == 'Create': response_data = client.subscribe_to_event(**params) if event['RequestType'] == 'Delete': response_data = client.unsubscribe_from_event(**params) if event['RequestType'] == 'Update': old_params = dict([(k, v) for k, v in event['OldResourceProperties'].items() if k != 'ServiceToken']) client.unsubscribe_from_event(**old_params) response_data = client.subscribe_to_event(**params) cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data) except Exception as e: cfnresponse.send(event, context, cfnresponse.FAILED, {}) Handler: index.handler MemorySize: 128 Role: !GetAtt AmazonInspectorFullAccessRole.Arn Runtime: python3.6 Timeout: 60
参照順が前後しますが、LambdaでInspectorを使用しますので、AWSLambdaBasicExecutionRole
+AmazonInspectorFullAccessRole
を定義して
Lambda関数に付与します。
AmazonInspectorFullAccessRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - "arn:aws:iam::aws:policy/AmazonInspectorFullAccess"
Lambdaができれば、これをカスタムリソースで呼び出します。
Propertiesから先のパラメータは、ResourceProperties
として、Lambdaに渡されるので
ここでは、subscribe_to_eventのパラメータをそのまま定義しています。
"ASSESSMENT_RUN_STARTED"を指定して、Inspectorのテンプレート開始時にSNS通知が飛ぶように設定しています。
CustomInspectorSubscribeToRunStarted: Type: Custom::InspectorSubscribeToEventLambda Version: 1.0 Properties: ServiceToken: !GetAtt InspectorSubscribeToEventLambda.Arn event: "ASSESSMENT_RUN_STARTED" resourceArn: !Ref MyInspectorTemplate topicArn: !Ref InspectorSnsTopic
resourceArn, topicArnについては、templeateに組み込む場合は
resourceArn: !Ref MyInspectorTemplate topicArn: !Ref InspectorSnsTopic
などとして、置き換えれます。
さて改めて、「Amazon Inspector を CloudFormationで一撃設定してみる。」ということで。 コンソールからまたは、CloudFormationを実行します。
今回Inspectorの設定する内容
対象
- 指定したタグが設定されたインスタンス 今回は"Inspector:true"がタグ付けされたインスタンス
ルール
- 共通脆弱性識別子
- CIS オペレーティングシステムのセキュリティ設定ベンチマーク
評価の実行の時間
- 3600秒(1時間)
SNS通知
- E-Mail通知
Inspector の実施間隔
- 7日間
CloudFormationの実行
下のリンクをクリックするとパラメータが入力された状態で、CloudFormatioのスタック作成画面が表示されます。 cfn-inspector-custom-sns
対象となる、タグキーと値ここではInspector:trueをデフォルトでいれてますので 対象の環境に応じてタグキーと値を変更してください。 また、SNS通知を行うE-Mailアドレスをパラメータとして入力します。
「AWS CloudFormationによってカスタム名のついた IAM リソースが作成される場合があることを承認します。」
のチェックをいれて実行します
結果確認
おお。手動で設定をする必要がなく、SNS設定が無事入ってますね。すばら。 (SNSトピックから承認通知がくるのでE-Mailで承認は実施ください)
削除確認
一応削除処理も実装していますので、スタックの削除を実施すれば綺麗に削除されますます。
まとめ
「この処理APIはあるけど、CloudFormationでまだサポートされていないんだよなー」という機会損失がなくなると良いです。 Lambdaが組める人はちょっと試してみてはいかかがでしょうか?
ただ、実際に本番で使用する際は、スタック削除のタイミングでAPIの挙動が変わっているかもしれない パラメータで機密情報渡したらどうなんの?などなどツッコミどころや考慮するべきところは多いかと思いますので ご利用は計画的に
本エントリーでは、 knakayama さんの下記ブログがかなり役に立ちました。 (追伸、knakayamaさん、また福岡遊びにきてください)
参考URL
Lambda-backed Custom Resourceを利用してCloudFormationにループ処理を擬似的に実装する
Lambda-Backed Custom Resourceを利用してServerless Application Modelで定義したLambda関数にDead Letter Queueを設定する